﻿using System.Globalization;
using Newtonsoft.Json;
using System.Text;
using System.Security.Cryptography;

namespace FileService.Infrastructure.Services
{
    internal class AliOSSStorageClient : IStorageClient
    {
        private IOptionsSnapshot<UpYunStorageOptions> options;
        public StorageType StorageType => StorageType.Public;
        public OssClient ossClient;
        public AliOSSStorageClient(IOptionsSnapshot<UpYunStorageOptions> options)
        {
            this.options = options;
            var opt = options.Value;
            ossClient = new OssClient(opt.Endpoint, opt.AccessKeyId, opt.AccessKeySecret);
        }
        private string FormatIso8601Date(DateTime dtime)
        {
            return dtime.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'",
                CultureInfo.CurrentCulture);
        }
        
        private string EncodeBase64(string code_type, string code)
        {
            string encode = "";
            byte[] bytes = Encoding.GetEncoding(code_type).GetBytes(code);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch
            {
                encode = code;
            }
            return encode;
        }
        
        private string ComputeSignature(string key, string data)
        {
            using (var algorithm = new HMACSHA1())
            {
                algorithm.Key = Encoding.UTF8.GetBytes(key.ToCharArray());
                return Convert.ToBase64String(
                    algorithm.ComputeHash(Encoding.UTF8.GetBytes(data.ToCharArray())));
            }
        }
        
        private string ToUnixTime(DateTime dtime)
        {
            const long ticksOf1970 = 621355968000000000;
            var expires = ((dtime.ToUniversalTime().Ticks - ticksOf1970) / 10000000L)
                .ToString(CultureInfo.InvariantCulture);

            return expires;
        }
        
        public Dictionary<string, string> GetPolicyAsync(CancellationToken cancellationToken = default)
        {

            string accessKeyId = options.Value.AccessKeyId;
            string accessKeySecret = options.Value.AccessKeySecret;
            string endpoint = options.Value.Endpoint;
            string bucketName = options.Value.BucketName;
            string pathRoot = options.Value.WorkingDir;
            int expireTime = options.Value.ExpireTime;

            Dictionary<string, string> policyDic = new Dictionary<string, string>();

            //expireTime
            var expireDateTime = DateTime.Now.AddSeconds(expireTime);
            DateTime today = DateTime.Today;
            //用日期把文件分散在不同文件夹存储，同时由于加上了文件hash值作为目录，又用用户上传的文件夹做文件名，
            //所以几乎不会发生不同文件冲突的可能
            //用用户上传的文件名保存文件名，这样用户查看、下载文件的时候，文件名更灵活
            string dir = $"{pathRoot}/{today.Year}/{today.Month}/{today.Day}";

            //policy
            var config = new PolicyConfig();
            config.expiration = FormatIso8601Date(expireDateTime);
            config.conditions = new List<List<Object>>();
            config.conditions.Add(new List<Object>());
            config.conditions[0].Add("content-length-range");
            config.conditions[0].Add(0);
            config.conditions[0].Add(5242880);
            config.conditions.Add(new List<Object>());
            config.conditions[1].Add("starts-with");
            config.conditions[1].Add("$key");
            config.conditions[1].Add(pathRoot);

            var policy = JsonConvert.SerializeObject(config);
            Console.WriteLine(policy);
            var policy_base64 = EncodeBase64("utf-8", policy);
            Console.WriteLine(policy_base64);
            var signature = ComputeSignature(accessKeySecret, policy_base64);
            
            policyDic.Add("OSSAccessKeyId", accessKeyId);
            policyDic.Add("policy", policy_base64);
            policyDic.Add("Signature", signature);
            policyDic.Add("dir", dir);
            policyDic.Add("host", $"https://{bucketName}.{endpoint}");
            policyDic.Add("expire", ToUnixTime(expireDateTime));
            

            return policyDic;
        }
    }
}
